#include <iostream>
#include <climits>
template <typename T>
struct twoPoint{
T *p1, *p2;
twoPoint(T* _p1, T* _p2): p1(_p1), p2(_p2) {}
~twoPoint(){
delete[] p1, p2;
}
};
struct Table{
int size;
int** table;
Table(int _size): size(_size){
this->table=new int*[this->size];
for(int i=0; i<this->size; ++i){
this->table[i]=new int[this->size];
}
}
~Table(){
for(int i=0; i<this->size; ++i) delete[] this->table[i];
delete[] this->table;
}
int* operator[](int n){
return this->table[n];
}
};
twoPoint<Table> MatrixChainOrder(int* p, int size){
int n=size-1;
Table* m=new Table(n);
Table* s=new Table(n-1);
for(int i=0; i<n; ++i) (*m)[i][i]=0;
for(int l=2; l<=n; ++l){
for(int i=0; i<n-l+1; ++i){
int j=i+l-1;
(*m)[i][j]=INT_MAX;
for(int k=i; k<j; ++k){
int q=(*m)[i][k]+(*m)[k+1][j]+p[i]*p[k+1]*p[j+1];
if(q<(*m)[i][j]){
(*m)[i][j]=q;
(*s)[i][j-1]=k+1;
}
}
}
}
return twoPoint<Table>(m, s);
}
void PrintOptimalParens(Table* s, int i, int j){
if(i==j) std::cout<<"A"<<i;
else{
std::cout<<"(";
PrintOptimalParens(s, i, (*s)[i-1][j-2]);
PrintOptimalParens(s, (*s)[i-1][j-2]+1, j);
std::cout<<")";
}
}
int main(void){
int p[]={30, 35, 15, 5, 10, 20, 25};
int size=sizeof(p)/ sizeof(int);
twoPoint<Table> res=MatrixChainOrder(p, size);
PrintOptimalParens(res.p2, 1, 6);
return 0;
}